package com.blogspot.mikelaud.cbs;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.FileVisitOption;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.EnumSet;

import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.MavenProject;

public class Makefile {

	private final String FILENAME = "Makefile";
	
	private Log mLog = null;
	private File mDirectory = null;
	private MavenProject mProject = null;
	//
	private MakefileInfo mInfo = null;
	
	private MakefileInfo createMakefileInfo() {
		MakefileInfo info = new MakefileInfo();
		//
		info.setModuleName(mProject.getArtifactId());
		//
		info.setVarProgram(info.getModuleName() + MakefileConst.VAR_PROGRAM_SUFFIX);
		info.setVarInclude(info.getModuleName() + MakefileConst.VAR_INCLUDE_SUFFIX);
		info.setVarSrc(info.getModuleName() + MakefileConst.VAR_SRC_SUFFIX);
		info.setVarCppflags(info.getModuleName() + MakefileConst.VAR_CPPFLAGS_SUFFIX);
		//
		info.setValueProgram("$(" + info.getVarProgram() + ")");
		info.setValueInclude("$(" + info.getVarInclude() + ")");
		info.setValueSrc("$(" + info.getVarSrc() + ")");
		info.setValueCppflags("$(" + info.getVarCppflags() + ")");
		//
		info.setValueCompiler("$(" + MakefileConst.DEFAULT_COMPILER + ")");
		//
		return info;
	}
	
	/*
	 * cbs-test_PROGRAM = cbs-test
	 */
	private String generateProgram() {
		StringBuilder b = new StringBuilder();
		b.append(mInfo.getVarProgram());
		b.append(" = ");
		b.append(mInfo.getModuleName());
		b.append("\n");
		return b.toString();
	}

	/*
	 * cbs-test_INCLUDE = include
	 */
	private String generateInclude() {
		StringBuilder b = new StringBuilder();
		b.append(mInfo.getVarInclude());
		b.append(" = ");
		b.append(MakefileConst.DEFAULT_INCLUDE_DIR);
		b.append("\n");
		return b.toString();
	}

	private String generateSrcFilenames() throws IOException {
		Path srcDir = Paths.get(mDirectory.getCanonicalPath(), MakefileConst.DEFAULT_SRC_DIR);
		EnumSet<FileVisitOption> options = EnumSet.of(FileVisitOption.FOLLOW_LINKS);
		SrcFilenamesGenerator generator = new SrcFilenamesGenerator();
		Files.walkFileTree(srcDir, options, Integer.MAX_VALUE, generator);
		return generator.getSrcFilenames();
	}
	
	/*
	 * cbs-test_SRC = src/Main.cpp src/Test.cpp
	 */
	private String generateSrc() throws IOException {
		StringBuilder b = new StringBuilder();
		b.append(mInfo.getVarSrc());
		b.append(" = ");
		b.append(generateSrcFilenames());
		b.append("\n");
		return b.toString();
	}
	
	/*
	 * cbs-test_CPPFLAGS = -O2
	 */
	private String generateCppflags() {
		StringBuilder b = new StringBuilder();
		b.append(mInfo.getVarCppflags());
		b.append(" = ");
		b.append(MakefileConst.DEFAULT_CPPFLAGS);
		b.append("\n");
		return b.toString();
	}
	
	/*
	all:
		$(CXX) $(cbs-test_CPPFLAGS) \
			-I$(cbs-test_INCLUDE) \
			-o $(cbs-test_PROGRAM) \
			$(cbs-test_SRC)
	*/
	private String generateAll() {
		StringBuilder b = new StringBuilder();
		b.append("all:\n");
		//
		b.append("\t");
		b.append(mInfo.getValueCompiler());
		b.append(" ");
		b.append(mInfo.getValueCppflags());
		b.append(" \\\n");
		//
		b.append("\t\t");
		b.append("-I");
		b.append(mInfo.getValueInclude());
		b.append(" \\\n");
		//
		b.append("\t\t");
		b.append("-o ");
		b.append(mInfo.getValueProgram());
		b.append(" \\\n");
		//
		b.append("\t\t");
		b.append(mInfo.getValueSrc());
		b.append("\n");
		//
		return b.toString();
	}
	
	/*
	clean:
		rm -f $(cbs-test_PROGRAM)
	*/
	private String generateClean() {
		StringBuilder b = new StringBuilder();
		b.append("clean:\n");
		//
		b.append("\t");
		b.append("rm -f ");
		b.append(mInfo.getValueProgram());
		b.append("\n");
		//
		return b.toString();
	}
	
	private void write(BufferedWriter w) throws IOException {
		w.write(generateProgram());
		w.write(generateInclude());
		w.write(generateSrc());
		w.write(generateCppflags());
		w.write("\n");
		w.write(generateAll());
		w.write("\n");
		w.write(generateClean());
	}
	
	public void setLog(Log aLog) { mLog = aLog; }
	public void setDirectory(File aDirectory) { mDirectory = aDirectory; }
	public void setProject(MavenProject aProject) { mProject = aProject; }
	
	public void generate() throws MojoExecutionException {
		if (null == mLog || null == mDirectory || null == mProject) {
			throw new MojoExecutionException("Invalid state of Makefile object.");
		}
		mInfo = createMakefileInfo();
		//
        if (! mDirectory.exists()) {
        	mDirectory.mkdirs();
        }
        File file = new File(mDirectory, FILENAME);
        FileWriter w = null;
        BufferedWriter bw = null;
        try {
            w = new FileWriter(file);
            bw = new BufferedWriter(w);
            write(bw);
            bw.flush();
            mLog.info("Create: " + file.getCanonicalPath());
        }
        catch (IOException e) {
            throw new MojoExecutionException("Error creating: " + file, e);
        }
        finally {
            if (bw != null) {
                try {
                   bw.close();
                }
                catch (IOException e) {
                    mLog.warn(e);
                }
            }
        }
	}
	
}
